package main

import (
	"fmt"
	"sync"
	"time"
	"unsafe"
)

func trap1() (result int) {
	defer func() { result++ }()
	return 2
}

func trap2() (result int) {
	t := 5
	defer func() { t += 5 }()
	return t
}

func trap3() (result int) {
	defer func(r int) { r += 5 }(result)
	return 1
}

//NOTE: DO NOT USE
// go func(){
// }()
//
// USING
//
// func1() {
// }
// go func1()
//
// This trap is frequent appearances
func trap4() {
	for i := 0; i < 2; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
	time.Sleep(1 * time.Second)

	for i := 0; i < 2; i++ {
		go func(i int) {
			fmt.Println(i)
		}(i)
	}
	time.Sleep(1 * time.Second)
}

var done chan struct{}

func trap5() {
	fmt.Println("done")
	done <- struct{}{}
}

func trap6() {
	var (
		s string = "cc"
	)
	{
		b := []byte(s)
		fmt.Printf("%p: %v, %p: %v\n", b, b, &s, s)
		b[0], b[1] = 'a', 'a'
		fmt.Printf("%p: %v, %p: %v\n", b, b, &s, s)
		b = append(b, "dd"...)
		fmt.Printf("%p: %v, %p: %v\n", b, b, &s, s)
	}
	{
		b := (*[]byte)(unsafe.Pointer(&s))
		fmt.Printf("%p: %v, %p: %v\n", *b, *b, &s, s)
		// string address is const address
		//(*b)[0], (*b)[1] = 'a', 'a'
		//fmt.Printf("%p: %v, %p: %v\n", *b, *b, &s, s)
		//*b = append(*b, "dd"...)
	}
}

// panic defer unlock
func trap7() {
	flag := false
	var lock sync.Mutex
	defer func() {
		if err := recover(); err != nil {
			if !flag {
				fmt.Println("i'm die without unlock")
			} else {
				fmt.Println("i'm die but unlock success")
			}
		}
	}()

	func() {
		fmt.Println("i'm lock")
		lock.Lock()
		defer func() {
			flag = true
			fmt.Println("i'm unlock")
			lock.Unlock()
		}()

		panic("i'm panic")

		// lock.Unlock() // missed unlock
	}()
}

// goroutine panic defer
func trap8() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("i'm die, but recovered.", err)
		}
	}()

	fmt.Println("i'm running")

	go func() {
		// panic("i'm panic") // it's will panic, and cann't be recover by trap8() defer recover().
	}()
	time.Sleep(10 * time.Millisecond)
}

func trap9() {
	{
		a1 := make([]int, 1, 1)
		a2 := append(a1, 1)
		fmt.Println(a1, a2)
		a3 := append(a1, 2)
		fmt.Println(a1, a2, a3)
	}
	{
		a1 := make([]int, 1, 2)
		a2 := append(a1, 1)
		fmt.Println(a1, a2)
		a3 := append(a1, 2)
		fmt.Println(a1, a2, a3) // !!! a2 unexpected changed
	}
	{
		a1 := make([]int, 1, 2)
		t1 := a1
		a2 := append(t1, 1)
		fmt.Println(a1, a2)
		t2 := a1
		a3 := append(t2, 2)
		fmt.Println(a1, a2, a3) // !!! a2 unexpected changed
	}
	{
		a1 := make([]int, 0)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
		a1 = append(a1, 1)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
		a1 = append(a1, 1)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
		a1 = append(a1, 1)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
		a1 = append(a1, 1)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
		a1 = append(a1, 1)
		fmt.Printf("%p %d %d\n", a1, len(a1), cap(a1))
	}
}

func trap10() {
	{
		tformat := "2006-01-02 15:04:05"
		now := time.Now().Truncate(time.Second)
		sNow := now.Format(tformat)
		fmt.Println("time.Format:", sNow)
		t, _ := time.Parse(tformat, sNow)
		fmt.Println("time.Parse Sub Now:", now.Sub(t).Seconds())
		t, _ = time.ParseInLocation(tformat, sNow, time.Local)
		fmt.Println("time.ParseInLocation Sub Now:", now.Sub(t).Seconds())
	}

	fmt.Println()

	{
		tformat := time.RFC3339
		now := time.Now().Truncate(time.Second)
		sNow := now.Format(tformat)
		fmt.Println("time.Format:", sNow)
		t, _ := time.Parse(tformat, sNow)
		fmt.Println("time.Parse Sub Now:", now.Sub(t).Seconds())
		t, _ = time.ParseInLocation(tformat, sNow, time.Local)
		fmt.Println("time.ParseInLocation Sub Now:", now.Sub(t).Seconds())
	}
}

func trap11() {
	a := []int{1, 2, 3}

	{
		ref := []*int{}
		for _, v := range a {
			ref = append(ref, &v) // !!! NOTE: Don't use range value address
		}
		for _, v := range ref {
			fmt.Printf("%d ", *v)
		}
		fmt.Println()
	}
	{
		// The right way
		ref := []*int{}
		for i := range a {
			ref = append(ref, &a[i])
		}
		for _, v := range ref {
			fmt.Printf("%d ", *v)
		}
		fmt.Println()
	}
}

func trap12() int {
	// NOTE: IMPORTANT !!!
	timeout := 1 * time.Second
	ch := make(chan int)
	// ch := make(chan int, 1) // right way
	go func() {
		// goroutine leak
		fmt.Println("start goroutine")
		defer fmt.Println("end goroutine")

		result := func() int {
			// a long time process
			time.Sleep(2 * time.Second)
			return 1
		}()
		ch <- result // blocks
	}()

	select {
	case result := <-ch: // process success
		return result
	case <-time.After(timeout): // process timeout
		return -1
	}
}

func main() {
	fmt.Println("------------trap1-------------")
	fmt.Println(trap1())
	fmt.Println("------------trap2-------------")
	fmt.Println(trap2())
	fmt.Println("------------trap3-------------")
	fmt.Println(trap3())
	fmt.Println("------------trap4-------------")
	trap4()
	fmt.Println("------------trap5-------------")
	// trap5
	// deadlock, done is a new variable, not the global variable
	//done := make(chan struct{})
	done = make(chan struct{})
	go trap5()
	<-done
	fmt.Println("------------trap6-------------")
	trap6()
	fmt.Println("------------trap7-------------")
	trap7()
	fmt.Println("------------trap8-------------")
	trap8()
	fmt.Println("------------trap9-------------")
	trap9()
	fmt.Println("------------trap10-------------")
	trap10()
	fmt.Println("------------trap11-------------")
	trap11()
	fmt.Println("------------trap12-------------")
	trap12()
}
